/*-------------------------------------------------------------------------
 *
 *	  UTF8 -> PinYin
 *
 * Created By Jason Zou <jason.zou@gmail.com> 2008-03-01
 * to-do list: utf8_to_pinyin function can be cleanned up. 
 *
 *
 *-------------------------------------------------------------------------
 */

#include <string.h>
#include "postgres.h"
#include "fmgr.h"
#include "utf8_to_pinyin.h"
#include "utf8_to_pinyin.map"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

PG_FUNCTION_INFO_V1(utf8_pinyin);

extern Datum utf8_pinyin(PG_FUNCTION_ARGS);

/*
 * comparison routine for bsearch()
 */
static int compareints (const void * a, const void * b){
  return ( *(unsigned int*)a - *(unsigned int*)b );
}

int pg_utf_mblen(const unsigned char *s){
  int len;

  if ((*s & 0x80) == 0)
    len = 1;
  else if ((*s & 0xe0) == 0xc0)
    len = 2;
  else if ((*s & 0xf0) == 0xe0)
    len = 3;
  else if ((*s & 0xf8) == 0xf0)
    len = 4;
  else if ((*s & 0xfc) == 0xf8)
    len = 5;
  else if ((*s & 0xfe) == 0xfc)
    len = 6;
  else
    len = 1;
  return len;
}

void utfToPinyin(const unsigned char *utf, unsigned char *pinyin)
{
  unsigned int iutf;
  int		l;
  utf8_to_pinyin *p;
  int len = strlen(utf);
  unsigned char temp[2*MAX_CHARS];
  int count = 0;
  int ii = 0;
        
  temp[0] = '\0';
        
  for (; len > 0; len -= l){
    if (count > MAX_CHARS)
      break;
     
	  /* "break" cases all represent errors */
    if (*utf == '\0')
      break;

    l = pg_utf_mblen(utf);

    if (len < l)
      break;

    //if (!pg_utf8_islegal(utf, l))
    //	break;

    if (l == 1){
      /* ASCII case is easy */
      temp[count++] = *utf++;
      continue;
    }else if (l == 2){
      iutf = *utf++ << 8;
      iutf |= *utf++;
    }else if (l == 3){
      iutf = *utf++ << 16;
      iutf |= *utf++ << 8;
      iutf |= *utf++;
    }else if (l == 4){
      iutf = *utf++ << 24;
      iutf |= *utf++ << 16;
      iutf |= *utf++ << 8;
      iutf |= *utf++;
    }

    p = bsearch(&iutf, LUmapPinYin, sizeof(LUmapPinYin)/sizeof(utf8_to_pinyin),
                sizeof(utf8_to_pinyin), compareints);

    if (p == NULL)
      continue;

    temp[count]='\0';
    ii = strlen(p->pinyin);
    strncat( temp, p->pinyin,ii);
    count += ii;
    temp[count]='\0';
  }

  if (count > MAX_CHARS){
    count = MAX_CHARS;
  }
  strncpy(pinyin,temp, count);
  pinyin[count]='\0';
}

Datum
utf8_pinyin(PG_FUNCTION_ARGS)
{
  text *src;
  unsigned char result[MAX_CHARS];
  text * realResult;
  int32 returnLen;
  int my;
  text *t;
  text *new_t;
 
  if (PG_ARGISNULL(0)){
    PG_RETURN_NULL();
  }else{
    src = PG_GETARG_TEXT_P(0);    
  }
  result[0]='\0';
  t = src;

  /* VARSIZE is the total size of the struct in bytes. */
  new_t = (text *) palloc(VARSIZE(t));
  my = VARSIZE(t);
  memset(new_t,'\0', VARSIZE(t));
  VARATT_SIZEP(new_t) = VARSIZE(t);

  /* VARDATA is a pointer to the data region of the struct. */
  memcpy((void *) VARDATA(new_t), /* destination */
         (void *) VARDATA(t), /* source */
         VARSIZE(t) - VARHDRSZ);   
  VARDATA(new_t)[VARSIZE(t)-VARHDRSZ] = '\0';

  /*ereport( NOTICE, ( errcode( ERRCODE_SUCCESSFUL_COMPLETION ), errmsg( "src--: %s,%d\n", VARDATA(new_t), my ))); */

  utfToPinyin(VARDATA(new_t), result);

  returnLen = strlen(result);
  realResult = (text *)palloc(returnLen + VARHDRSZ);
  VARATT_SIZEP(realResult) = returnLen + VARHDRSZ;
  /*ereport( NOTICE, ( errcode( ERRCODE_SUCCESSFUL_COMPLETION ), errmsg( "dest--: %s, %d\n", result, VARSIZE(realResult) )));*/

  memset((void *)VARDATA(realResult), '\0', returnLen);
  memcpy((void *)VARDATA(realResult), (void *)result, returnLen);
  /* ereport( NOTICE, ( errcode( ERRCODE_SUCCESSFUL_COMPLETION ), errmsg( "result--: %s,%d\n", VARDATA(realResult), VARSIZE(realResult) )));*/
 
  PG_RETURN_TEXT_P(realResult);
}
